﻿namespace NServiceBus.AcceptanceTests.Sagas;

using System;
using System.Threading.Tasks;
using AcceptanceTesting;
using EndpointTemplates;
using NUnit.Framework;

public class When_saga_handles_unmapped_message : NServiceBusAcceptanceTest
{
    [Test]
    public async Task Should_throw_on_unmapped_uncorrelated_msg()
    {
        var id = Guid.NewGuid();

        var context = await Scenario.Define<Context>()
            .WithEndpoint<UnmappedMsgEndpoint>(b =>
            {
                b.DoNotFailOnErrorMessages();

                b.When(session => session.SendLocal(new StartSagaMessage
                {
                    SomeId = id
                }));
            })
            .Done(c => c.MappedEchoReceived && (c.EchoReceived || !c.FailedMessages.IsEmpty))
            .Run();

        using (Assert.EnterMultipleScope())
        {
            Assert.That(context.StartReceived, Is.True);
            Assert.That(context.OutboundReceived, Is.True);
            Assert.That(context.MappedEchoReceived, Is.True);
            Assert.That(context.EchoReceived, Is.False);
            Assert.That(context.FailedMessages, Has.Count.EqualTo(1));
        }
    }

    public class Context : ScenarioContext
    {
        public bool StartReceived { get; set; }
        public bool OutboundReceived { get; set; }
        public bool EchoReceived { get; set; }
        public bool MappedEchoReceived { get; set; }
    }

    public class UnmappedMsgEndpoint : EndpointConfigurationBuilder
    {
        public UnmappedMsgEndpoint() => EndpointSetup<DefaultServer>();

        public class UnmappedMsgSaga(Context testContext) : Saga<UnmappedMsgSagaData>,
            IAmStartedByMessages<StartSagaMessage>,
            IHandleMessages<MappedEchoMessage>,
            IHandleMessages<EchoMessage>
        {
            protected override void ConfigureHowToFindSaga(SagaPropertyMapper<UnmappedMsgSagaData> mapper)
            {
                mapper.MapSaga(s => s.SomeId)
                    .ToMessage<StartSagaMessage>(msg => msg.SomeId)
                    .ToMessage<MappedEchoMessage>(msg => msg.SomeId);
                // No mapping for EchoMessage, so saga can't possibly be found
            }

            public Task Handle(StartSagaMessage message, IMessageHandlerContext context)
            {
                testContext.StartReceived = true;
                return context.SendLocal(new OutboundMessage { SomeId = message.SomeId });
            }

            public Task Handle(MappedEchoMessage message, IMessageHandlerContext context)
            {
                testContext.MappedEchoReceived = true;
                return Task.CompletedTask;
            }

            public Task Handle(EchoMessage message, IMessageHandlerContext context)
            {
                testContext.EchoReceived = true;
                return Task.CompletedTask;
            }
        }

        public class UnmappedMsgSagaData : ContainSagaData
        {
            public virtual Guid SomeId { get; set; }
        }

        public class OutboundMessageHandler(Context testContext) : IHandleMessages<OutboundMessage>
        {
            public async Task Handle(OutboundMessage message, IMessageHandlerContext context)
            {
                testContext.OutboundReceived = true;
                await context.SendLocal(new EchoMessage { SomeId = message.SomeId });
                await context.SendLocal(new MappedEchoMessage { SomeId = message.SomeId });
            }
        }
    }

    public class StartSagaMessage : ICommand
    {
        public Guid SomeId { get; set; }
    }

    public class OutboundMessage : ICommand
    {
        public Guid SomeId { get; set; }
    }

    public class EchoMessage : ICommand
    {
        public Guid SomeId { get; set; }
    }

    public class MappedEchoMessage : ICommand
    {
        public Guid SomeId { get; set; }
    }
}
